# import runnable as runnable  # 注释掉这个导入，因为它会导致编码问题
from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from pydantic import BaseModel, Field
from typing import List, Dict, Optional
from enum import Enum
import json
import os
import time
from langchain.chat_models import init_chat_model
from langchain_openai import ChatOpenAI
from openai import APIConnectionError, APITimeoutError
from dashscope import Generation
from dashscope.api_entities.dashscope_response import DashScopeAPIResponse


# 定义排序字段枚举：支持按流量(data)或价格(price)排序
class SortEnum(str, Enum):
    data = 'data'
    price = 'price'


# 定义排序方式枚举：支持升序(ascend)或降序(descend)
class OrderingEnum(str, Enum):
    ascend = 'ascend'
    descend = 'descend'


# 语义解析输出结构定义
# 用于将用户自然语言输入映射为结构化查询参数
class Semantics(BaseModel):
    name: Optional[str] = Field(description="流量包名称", default=None)
    price_lower: Optional[int] = Field(description="价格下限", default=None)
    price_upper: Optional[int] = Field(description="价格上限", default=None)
    data_lower: Optional[int] = Field(description="流量下限", default=None)
    data_upper: Optional[int] = Field(description="流量上限", default=None)
    sort_by: Optional[SortEnum] = Field(description="按价格或流量排序", default=None)
    ordering: Optional[OrderingEnum] = Field(
        description="升序或降序排列", default=None)


# 构建提示词模板，用于引导模型进行语义解析任务
# system角色设定模型任务目标，human角色接收用户输入文本
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个语义解析器。你的任务是将用户的输入解析成JSON表示。不要回答用户的问题。"),
        ("human", "{text}"),
    ]
)




# 从环境变量获取通义千问API密钥
dashscope_api_key = os.getenv("DASHSCOPE_API_KEY", "sk-2c036d80d14a443f8769e072bb09a2e1")

# 初始化聊天模型，使用通义千问qwen-plus模型进行语义解析
# 通过ChatOpenAI的兼容模式连接到通义千问API
llm = ChatOpenAI(
    api_key=dashscope_api_key,
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model="qwen-plus",
    temperature=0.1  # 设置较低的温度以获得更稳定的输出
)

# 将模型包装为结构化输出格式，绑定输出结构Semantics
# 指定使用 function_calling 方法，因为 gpt-3.5-turbo 不支持 structured output API
structured_llm = llm.with_structured_output(Semantics, method="function_calling")

# 构建LangChain表达式语言(LCEL)链：
# 1. 将输入直接传递给text变量
# 2. 使用prompt模板格式化输入
# 3. 调用结构化模型生成输出
runnable = (
        {"text": RunnablePassthrough()} | prompt | structured_llm
)

def invoke_with_retry(runnable, input_text, max_retries=3, delay=2):
    """
    带重试机制的调用函数
    """
    for attempt in range(max_retries):
        try:
            print(f"[INFO] 尝试第 {attempt + 1} 次调用...")
            ret = runnable.invoke(input_text)
            return ret
        except (APIConnectionError, APITimeoutError, Exception) as e:
            print(f"[ERROR] 第 {attempt + 1} 次调用失败: {str(e)}")
            if attempt < max_retries - 1:
                print(f"[INFO] 等待 {delay} 秒后重试...")
                time.sleep(delay)
                delay *= 2  # 指数退避
            else:
                print("[ERROR] 所有重试都失败了")
                raise
        except Exception as e:
            print(f"[ERROR] 发生未知错误: {str(e)}")
            raise

# 执行语义解析任务，将自然语言查询转换为结构化对象
try:
    ret = invoke_with_retry(runnable, "不超过100元的流量大的套餐有哪些")
    
    # 将结果以格式化JSON形式打印输出
    print("[SUCCESS] 解析成功！")
    print(json.dumps(
        ret.model_dump(),
        indent=4,
        ensure_ascii=False
    ))
except Exception as e:
    print(f"[ERROR] 最终执行失败: {str(e)}")
    print("[INFO] 建议检查：")
    print("   1. 网络连接是否正常")
    print("   2. 通义千问API密钥是否有效")
    print("   3. DashScope API服务是否可用")
    print("   4. 检查DASHSCOPE_API_KEY环境变量")